﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
using System.Text.RegularExpressions;
using System.Reflection;
using FBEntities.FBConnections;
using System.IO;
using SynqIt;
using System.Globalization;

namespace FBEntities.FBObjects
{
    public delegate void UpdateEventHandler(object sender, EventArgs e);
    public interface IFBObject
    {
        //event EventHandler FinishParse;

        void Parse(JObject text);
        //void ParseAsync(JObject text);
    }

    public interface IFBUpdatable : IFBObject
    {
        event UpdateEventHandler Updated;
    }

    public class FBObject : IFBObject
    {
        public long id;
        public string name;

        #region IFBObject Members

        public void Parse(JObject o)
        {
            foreach (var field in GetType().GetFields())
            {
                if (o[field.Name] == null || o[field.Name].Type == JTokenType.Null)
                    continue;

                if (field.FieldType.Equals(typeof(long)))
                {
                    var valType = o[field.Name].Type;
                    if (valType == JTokenType.String)
                    {
                        if (!string.IsNullOrEmpty((string)o[field.Name]))
                            field.SetValue(this, long.Parse((string)o[field.Name]));
                    }
                    else
                        field.SetValue(this, (long)o[field.Name]);
                }
                else if (field.FieldType.Equals(typeof(string)))
                    field.SetValue(this, (string)o[field.Name]);
            }
        }

        #endregion
    }

    public static class FBParseUtilities
    {
        private static string curDir;
        private static string restApiUrl = "https://api.facebook.com/method";

        public static string CurDir
        {
            get
            {
                if (string.IsNullOrEmpty(curDir))
                    curDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
                return curDir;
            }
        }
        public static string authKey;

        public static bool Like(string id)
        {
            var authKeyCode = authKey.Split('=').Last();
            var args = new Dictionary<string, string>();
            args.Add("auth_key", authKeyCode);

            string ret = WebDownloader.MakeRequest(new Uri(string.Format("https://graph.facebook.com/{0}/likes", id)), HttpVerb.POST, args);

            return ret.Contains("OAuthException");
        }

        public static void Unlike(string id)
        {
            var authKeyCode = authKey.Split('=').Last();
            var args = new Dictionary<string, string>();
            args.Add("auth_key", authKeyCode);

            string ret = WebDownloader.MakeRequest(new Uri(string.Format("https://graph.facebook.com/{0}/likes", id)), HttpVerb.DELETE, args);
        }

        public static void PostText(string id, string type, string text)
        {
            var authKeyCode = authKey.Split('=').Last();
            var args = new Dictionary<string, string>();
            args.Add("auth_key", authKeyCode);
            args.Add("message", text);

            string ret = WebDownloader.MakeRequest(new Uri(string.Format("https://graph.facebook.com/{0}/{1}", id, type)), HttpVerb.POST, args);
        }

        public static bool LikeREST(string id, bool remove)
        {
            var authKeyCode = authKey.Split('=').Last();
            var args = new Dictionary<string, string>();
            args.Add("access_token", authKeyCode);
            args.Add("post_id", id);
            string method = "stream.addLike";
            if (remove)
                method = "stream.removeLike";


            string ret = WebDownloader.MakeRequest(new Uri(string.Format("{0}/{1}", restApiUrl, method)), HttpVerb.GET, args);
            return !ret.Contains("OAuthException");
        }

        public static string PostTextREST(string id, string method, string[] text)
        {
            var authKeyCode = authKey.Split('=').Last();
            var args = new Dictionary<string, string>();
            args.Add("access_token", authKeyCode);
            args.Add(text[0], text[1]);
            args.Add("post_id", id);

            string ret = WebDownloader.MakeRequest(new Uri(string.Format("{0}/{1}", restApiUrl, method)), HttpVerb.GET, args);
            return ret;
        }

        public static void Parse(JObject o, object item)
        {
            if (o == null)
                return;

            foreach (var field in item.GetType().GetFields())
            {
                if (o[field.Name] == null)
                {

                    if (field.FieldType.Equals(typeof(int)))
                        field.SetValue(item, 0);
                    else if (field.FieldType.Equals(typeof(string)))
                        field.SetValue(item, "");
                    else if (field.FieldType.Equals(typeof(long)))
                        field.SetValue(item, 0);
                    else if (field.FieldType.Equals(typeof(DateTime)))
                        field.SetValue(item, new DateTime());
                    else if (field.FieldType.GetInterfaces().Count(i => i.Equals(typeof(IFBObject))) > 0)
                        field.SetValue(item, null);
                    continue;
                }

                if (testPrims(field, o, item))
                    continue;
                else if (testObjs(field, o, item))
                    continue;
                else if (testArrays(field, o, item))
                    continue;
            }
        }

        private static bool testArrays(FieldInfo field, JObject o, object item)
        {
            if (field.FieldType.GetGenericArguments().Count() == 0)
                return false;

            var arg = field.FieldType.GetGenericArguments()[0];

            if (arg.GetInterfaces().Count(i => i.Equals(typeof(IFBObject))) > 0)
            {
                Type gen = field.FieldType.GetGenericArguments()[0];
                var clear = field.FieldType.GetMethod("Clear");
                clear.Invoke(field.GetValue(item), null);

                var method = field.FieldType.GetMethod("Add");

                var array = o[field.Name] as JArray;

                for (int i = 0; i < array.Count; i++)
                {
                    if (array[i].Type == JTokenType.Null)
                        continue;

                    var obj = (IFBObject)Activator.CreateInstance(gen);
                    obj.Parse((JObject)array[i]);

                    method.Invoke(field.GetValue(item), new object[] { obj });
                }
            }
            else return false;

            return true;
        }

        private static bool testObjs(FieldInfo field, JObject o, object item)
        {
            if (field.FieldType.Equals(typeof(DateTime)))
            {
                Regex dateReg = new Regex(@"(?<Year>[0-9]{4})-(?<Month>[0-9]{2})-(?<Day>[0-9]{2})T(?<Hour>[0-9]{2}):(?<Min>[0-9]{2}):(?<Sec>[0-9]{2})\+");  //2010-05-30T06:01:35+0000
                string date = (string)o[field.Name];
                var match = dateReg.Match(date);
                DateTime time = new DateTime(int.Parse(match.Groups["Year"].Value),
                    int.Parse(match.Groups["Month"].Value),
                    int.Parse(match.Groups["Day"].Value),
                    int.Parse(match.Groups["Hour"].Value),
                    int.Parse(match.Groups["Min"].Value),
                    int.Parse(match.Groups["Sec"].Value));
                field.SetValue(item, time);
            }   //  If the field has the IFBObject interface
            else if (field.FieldType.GetInterfaces().Count(i => i.Equals(typeof(IFBObject))) > 0)
            {
                IFBObject obj = (IFBObject)Activator.CreateInstance(field.FieldType);
                var data = o[field.Name] as JObject;
                obj.Parse(data);
                field.SetValue(item, obj);
            }
            else return false;

            return true;
        }

        private static bool testPrims(FieldInfo field, JObject o, object item)
        {
            if (field.FieldType.Equals(typeof(int)))
                field.SetValue(item, getJSONObject(o[field.Name], typeof(int)));
            else if (field.FieldType.Equals(typeof(string)))
                field.SetValue(item, getJSONObject(o[field.Name], typeof(string)));
            else if (field.FieldType.Equals(typeof(long)))
                field.SetValue(item, getJSONObject(o[field.Name], typeof(long)));
            else return false;

            return true;
        }

        private static object getJSONObject(JToken jToken, Type type)
        {
            object ret = null;
            switch (jToken.Type)
            {
                case JTokenType.Float:
                    float f = (float)jToken;
                    ret = f;
                    break;
                case JTokenType.Integer:
                    long i = (long)jToken;
                    ret = i;
                    break;
                case JTokenType.String:
                    string s = (string)jToken;
                    ret = s;
                    break;
            }

            if (!type.IsInstanceOfType(ret))
            {
                return Convert.ChangeType(ret, type, CultureInfo.CurrentCulture);
            }
            return ret;
        }

        private static void testConnection(FieldInfo field, JObject o, object item)
        {
            if (field.FieldType.GetInterfaces().Count(i => i.Equals(typeof(IFBConnection))) > 0)
            {
                IFBConnection obj = (IFBConnection)Activator.CreateInstance(field.FieldType);

                if (o["id"] != null)
                    obj.Parse((string)o["id"]);

                field.SetValue(item, obj);
            }
        }
    }
}
